// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 



using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;


namespace svs.Mobile.BeyondScheduler.UI
{
	internal class OwnerDrawnList : Control
	{
		#region Data Members

		private int scrollWidth;
		private int itemHeight = -1;
		private int selectedIndex = -1;
		private string m_OnEmptyText = "There are no items in this view";
		private Bitmap offScreen;
		private VScrollBar vs;
		private ArrayList items;
		protected bool EnableKeyNavigation = true;
		protected bool EnableMouseNavigation = true;
		public event EventHandler SelectedIndexChanged;
		public event EventHandler BackClick;
		protected Color HighlightColor = SystemColors.InactiveCaption;

		#endregion

		#region Constructors

		public OwnerDrawnList()
		{
			vs = new VScrollBar();
			scrollWidth = vs.Width;
			vs.Parent = this;
			vs.Visible = false;
			vs.SmallChange = 1;
			vs.ValueChanged += new EventHandler(ScrollValueChanged);

			// Items to draw
			items = new ArrayList();

			BackColor = Color.FromArgb(224, 236, 253);
		}

		#endregion

		#region Protected Properties

		protected Bitmap OffScreen
		{
			get { return offScreen; }
		}

		protected VScrollBar VScrollBar
		{
			get { return vs; }
		}

		protected virtual int ItemHeight
		{
			get { return itemHeight; }
			set { itemHeight = value; }
		}

		protected int DrawCount
		{
			get
			{
				if (vs.Value + vs.LargeChange > vs.Maximum)
					return vs.Maximum - vs.Value + 1;
				else
					return vs.LargeChange;
			}
		}

		#endregion

		#region Public Properties
		
		public virtual string OnEmptyText
		{
			get { return m_OnEmptyText; }
		}

		public int SelectedIndex
		{
			get { return selectedIndex; }

			protected set
			{
				selectedIndex = value;
				if (SelectedIndexChanged != null)
					SelectedIndexChanged(this, EventArgs.Empty);
			}
		}

		public ArrayList Items
		{
			get { return items; }
		}

		#endregion

		#region Public Methods

		public void EnsureVisible(int index)
		{
			if (index >= 0)
			{
				if (index < vs.Value)
				{
					vs.Value = index;
				}
				else if (index >= vs.Value + DrawCount)
				{
					vs.Value = index - DrawCount + 1;
				}
			}

			Refresh();

			if (Visible)
				Focus();
		}

		#endregion

		#region Protected Methods

		protected override void OnPaint(PaintEventArgs e)
		{
			OnPaintEmptyList(e);
		}

		protected void OnPaintEmptyList(PaintEventArgs e)
		{
			if (Items.Count == 0)
			{
				using (Font font = new Font(Font.Name, 10, FontStyle.Italic))
				{
					Rectangle rect = ClientRectangle;
					rect.Inflate(-10, -10);

					using (Brush b = new SolidBrush(Color.Gray))
					{
						e.Graphics.DrawString(OnEmptyText, font, b, rect);
					}
				}
			}
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);

			if (!EnableMouseNavigation)
				return;

			int itemTop = 0;

			for (int n = VScrollBar.Value; n <= VScrollBar.Value + DrawCount; n++)
			{
				Rectangle itemArea;
				itemArea = new Rectangle(0,
				                         itemTop,
				                         ClientSize.Width - (VScrollBar.Visible ? VScrollBar.Width : 0),
				                         ItemHeight);
				if (n > Items.Count - 1)
					break;

				if (itemArea.Contains(e.X, e.Y))
				{
					ItemClicked(n);
					break;
				}

				// Set the next item top to move down the item height
				itemTop += ItemHeight;
			}
		}

		protected virtual void ItemClicked(int i_ItemIndex)
		{
			SelectedIndex = i_ItemIndex;
			Refresh();
		}

		protected virtual void OnSelectedIndexChanged(EventArgs e)
		{
			if (SelectedIndexChanged != null)
				SelectedIndexChanged(this, e);
		}

		protected void ScrollValueChanged(object o, EventArgs e)
		{
			Refresh();
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			if (EnableKeyNavigation)
			{
				switch (e.KeyCode)
				{
					case Keys.Down:
						if (SelectedIndex < vs.Maximum)
						{
							++SelectedIndex;
						}
						else
						{
							SelectedIndex = vs.Minimum;
						}

						EnsureVisible(SelectedIndex);

						e.Handled = true;
						break;

					case Keys.Up:
						if (SelectedIndex > vs.Minimum)
						{
							--SelectedIndex;
						}
						else
						{
							SelectedIndex = vs.Maximum;
						}

						EnsureVisible(SelectedIndex);

						e.Handled = true;
						break;
				}

				//Patch the * and # symbols
				if((int)e.KeyData == 120)
					FindNextItemStartingWith('A');
				else if((int)e.KeyData == 119)
					FindNextItemStartingWith('Z');
			}

			base.OnKeyDown(e);
		}

		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			if (e.KeyChar == (Char) Keys.Escape)
			{
				e.Handled = true;
				if (BackClick != null)
					BackClick(this, null);
			}

			FindNextItemStartingWith(e.KeyChar);
		}

		protected override void OnResize(EventArgs e)
		{
			int viewableItemCount = ClientSize.Height/ItemHeight;

			vs.Bounds = new Rectangle(ClientSize.Width - scrollWidth,
			                          0,
			                          scrollWidth,
			                          ClientSize.Height);


			// Determine if scrollbars are needed
			if (items.Count > viewableItemCount)
			{
				vs.Visible = true;
				vs.LargeChange = viewableItemCount;
				offScreen = new Bitmap(ClientSize.Width - scrollWidth, ClientSize.Height);
			}
			else
			{
				vs.Visible = false;
				vs.LargeChange = items.Count;
				offScreen = new Bitmap(ClientSize.Width, ClientSize.Height);
			}

			vs.Maximum = items.Count - 1;
		}

		// Determine what the text color should be
		// for the selected item drawn as highlighted
		protected Color CalcTextColor(Color backgroundColor)
		{
			if (backgroundColor.Equals(Color.Empty))
				return Color.Black;

			int sum = backgroundColor.R + backgroundColor.G + backgroundColor.B;

			if (sum > 256)
				return Color.Black;
			else
				return Color.White;
		}

		protected bool FindNextItemStartingWith(char keyChar)
		{
			if (Items.Count == 0)
				return false;

			int start, curr;

			if (SelectedIndex > 0 && selectedIndex < Items.Count)
				curr = SelectedIndex;
			else
				curr = 0;

			start = curr;

			do
			{
				curr++;

				if (curr > Items.Count - 1)
					curr = 0;

				if (GetItemText(curr).StartsWith(keyChar.ToString(), StringComparison.InvariantCultureIgnoreCase)
					/*||
				    GetItemText(curr).StartsWith(Helpers.GetAlternativeKey(keyChar).ToString(),
				                                 StringComparison.InvariantCultureIgnoreCase)*/)
				{
					SelectedIndex = curr;
					EnsureVisible(SelectedIndex);
					return true;
				}
			} while (curr != start);

			return false;
		}

		protected virtual string GetItemText(int ItemIndex)
		{
			return string.Empty;
		}

		#endregion
	}
}